package com.yupi.service.impl;

import java.util.Date;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.common.ErrorCode;
import com.yupi.exception.BusinessException;
import com.yupi.mapper.TeamMapper;
import com.yupi.model.domain.Team;
import com.yupi.model.domain.UserTeam;
import com.yupi.model.domain.Userinfo;
import com.yupi.model.dto.TeamJoinDTO;
import com.yupi.model.dto.TeamQueryDTO;
import com.yupi.model.dto.TeamQuitDTO;
import com.yupi.model.dto.TeamUpdateDTO;
import com.yupi.model.enums.TeamStatusEnums;
import com.yupi.model.vo.TeamVO;
import com.yupi.model.vo.UserVO;
import com.yupi.service.TeamService;
import com.yupi.service.UserTeamService;
import com.yupi.service.UserinfoService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.apache.commons.collections4.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author 木兔光太郎
 * @description 针对表【team(队伍表)】的数据库操作Service实现
 * @createDate 2024-08-16 11:04:46
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
        implements TeamService {

    @Resource
    private UserTeamService userTeamService;

    @Resource
    private UserinfoService userinfoService;

    @Resource
    private RedissonClient redissonClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public long addTeam(Team team, Userinfo loginUser) {
        //判断队伍参数
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //判断是否已经登录
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "请先登录");
        }
        final Long userId = loginUser.getId();
        //开始校验各个参数是否合规
        //校验人数
        Integer maxNum = Optional.ofNullable(team.getMaxNum()).orElse(5);
        if (maxNum < 1 || maxNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数不满足要求");
        }
        //校验描述长度
        if (StringUtils.isNoneBlank(team.getDescription()) && team.getDescription().length() > 300) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述长度过长");
        }
        //队伍名称
        if (team.getName().length() < 4 || team.getName().length() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍名称长度不符");
        }
        //状态，如果为空的话默认为0
        Integer status = Optional.ofNullable(team.getStatus()).orElse(0);
        //如果传的参数再enums找不到就报错
        TeamStatusEnums enumByValue = TeamStatusEnums.getEnumByValue(status);
        if (enumByValue == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //如果状态为加密的话，则判断是否有密码且密码是否<10位
        if (TeamStatusEnums.PWD.equals(enumByValue)) {
            String password = team.getPassword();
            if (password.length() > 10 || StringUtils.isBlank(password)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码不符合要求");
            }

        }
        //设置的超时时间要大于当前时间
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "过期时间不能小于当前时间");
        }
        //检测当前用户已创建的队伍数量
        //todo 有bug，可能同时创建100个队伍
        QueryWrapper<Team> qw = new QueryWrapper<>();
        qw.eq("user_id", userId);
        long hasTeamNum = this.count(qw);
        if (hasTeamNum >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建5个队伍");
        }

        //插入队伍信息到队伍表
        team.setUserId(userId);
        team.setId(0l);
        boolean result = this.save(team);
        Long teamId = team.getId();

        //todo 队伍现有人数，以及有用户加入时增加队伍人数，同时关联队伍信息

        //插入信息到关联表
        if (!result || teamId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        UserTeam userTeam = new UserTeam();
        userTeam.setTeamId(team.getId());
        userTeam.setUserId(userId);
        userTeam.setJoinTime(new Date());
        boolean save = userTeamService.save(userTeam);
        if (!save) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        return teamId;
    }

    @Override
    public List<TeamVO> listTeams(TeamQueryDTO teamQueryDTO, boolean isAdmin) {
        QueryWrapper<Team> qw = new QueryWrapper<>();
        //条件判断
        Long id = teamQueryDTO.getId();
        if (id != null && id > 0) {
            qw.eq("id", id);
        }
        List<Long> idList = teamQueryDTO.getIdList();
        if (CollectionUtils.isNotEmpty(idList)) {
            qw.in("id", idList);
        }

        String name = teamQueryDTO.getName();
        if (StringUtils.isNotBlank(name)) {
            qw.like("name", name);
        }
        String description = teamQueryDTO.getDescription();
        if (StringUtils.isNotBlank(description)) {
            qw.like("description", description);
        }
        Integer maxNum = teamQueryDTO.getMaxNum();
        if (maxNum != null && maxNum > 0) {
            qw.eq("max_num", maxNum);
        }
        Long userId = teamQueryDTO.getUserId();
        if (userId != null && userId > 0) {
            qw.eq("user_id", userId);
        }
        //只有管理员才能查看加密的队伍
        Integer status = teamQueryDTO.getStatus();

        TeamStatusEnums statusEnum = TeamStatusEnums.getEnumByValue(status);
        if (statusEnum != null) {
            if (!isAdmin && statusEnum.equals(TeamStatusEnums.PRI)) {
                throw new BusinessException(ErrorCode.NO_AUTH);
            }
            int statusValue = statusEnum.getValue();
            qw.eq("status", statusValue);
        }


        //同时从两个字段匹配
        String searchText = teamQueryDTO.getSearchText();
        if (searchText != null) {
//            qw.and(q -> q.like("name", searchText).or().like("description", searchText));
            qw.like("name", searchText).or().like("description", searchText);
        }

        //不展示已过期的队伍
        //为空的话是不过期的队伍
        qw.and(q -> q.gt("expire_time", new Date()).or().isNull("expire_time"));
//        qw.eq("is_deleted", 0);
        List<Team> teamList = this.list(qw);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }

        List<TeamVO> teamVOList = new ArrayList<>();
        //查询创建人相关信息
        for (Team team : teamList) {
            Long createUserId = team.getUserId();
            if (createUserId == null) {
                continue;
            }
            Userinfo createUser = userinfoService.getById(createUserId);
            //转换用户、队伍信息到VO
            TeamVO teamVO = new TeamVO();
            UserVO userVO = new UserVO();
            //转换team信息
            BeanUtils.copyProperties(team, teamVO);
            if (createUser != null) {
                //转换用户信息
                BeanUtils.copyProperties(createUser, userVO);
            }

            //todo 关联查询已加入队伍的用户的信息(自己实现版)
            //从关系表中根据队伍id查出所有加入的用户
            Long teamId = team.getId();
            //找到用户加入当前队伍的记录
            QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("team_id", teamId);
            List<UserTeam> userTeamList = userTeamService.list(queryWrapper);
            //创建队员集合
            List<UserVO> joinTeamUsers = new ArrayList<>();
            //遍历所有加入当前队伍的用户
            for (UserTeam userTeam : userTeamList) {
                //获取队长id
                Long teamUserId = userTeam.getUserId();
                //判断是否为队伍的创建人
                if (!createUserId.equals(teamUserId)) {
                    //如果不是，则查数据库取到该用户的信息
                    Userinfo joinTeamUser = userinfoService.getById(teamUserId);
                    UserVO joinTeamUserVO = new UserVO();
                    BeanUtils.copyProperties(joinTeamUser, joinTeamUserVO);
                    joinTeamUsers.add(joinTeamUserVO);
                }
            }
            //队员的信息
            teamVO.setUserVOS(joinTeamUsers);
            //创建人信息
            teamVO.setCreateUser(userVO);
            //队伍的信息
            teamVOList.add(teamVO);

        }

        return teamVOList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateTeam(TeamUpdateDTO teamUpdateDTO, Userinfo loginUser) {
        //判断参数是否为空
        if (teamUpdateDTO == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //id是否合规
        Long teamId = teamUpdateDTO.getId();
        if (teamId <= 0 || teamId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "ID不符");
        }
        //该队伍是否存在
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "该队伍不存在");
        }
        //只有管理员以及队长可以修改队伍信息
        if (!team.getUserId().equals(loginUser.getId()) && !userinfoService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //如果team的status修改为私密状态，需要设置密码
        Integer status = teamUpdateDTO.getStatus();
        if (TeamStatusEnums.getEnumByValue(status).equals(TeamStatusEnums.PWD)) {
            if (StringUtils.isBlank(teamUpdateDTO.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密队伍必须要有密码");
            }
        }
        Team updateTeam = new Team();
        BeanUtils.copyProperties(teamUpdateDTO, updateTeam);
        boolean result = this.updateById(updateTeam);

        return result;
    }

    @Override
    public boolean joinTeam(TeamJoinDTO teamJoinDTO, Userinfo loginUser) {
        if (teamJoinDTO == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //队伍必须存在
        Long teamId = teamJoinDTO.getTeamId();
        if (teamId < 0 || teamId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "id不对劲");
        }
        Team joinTeam = this.getById(teamId);
        if (joinTeam == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "加入的队伍不存在");
        }
        //不能加入已过期队伍
        if (joinTeam.getExpireTime() != null && joinTeam.getExpireTime().before(new Date())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已过期");
        }
        //不能加入私有队伍
        Integer status = joinTeam.getStatus();
        if (TeamStatusEnums.PRI.equals(TeamStatusEnums.getEnumByValue(status))) {
            throw new BusinessException(ErrorCode.NO_AUTH, "您无权加入私有队伍");
        }
        //加密队伍的密码需要一致
        if (TeamStatusEnums.PWD.equals(TeamStatusEnums.getEnumByValue(status))) {
            String password = teamJoinDTO.getPassword();
            if (StringUtils.isBlank(password) || !joinTeam.getPassword().equals(password)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码不对哦");
            }
        }
        //分布式锁
        RLock lock = redissonClient.getLock("dogfriend:jointeam:lock");
        try {
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    //最多创建、加入五个队伍
                    Long userId = loginUser.getId();

                    QueryWrapper<UserTeam> qw = new QueryWrapper<>();
                    qw.eq("user_id", userId);
                    long joinTeamNum = userTeamService.count(qw);
                    if (joinTeamNum >= 5) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建|加入队伍太多啦");
                    }

                    //只能加入未满的队伍

                    long peopleNumTeam = getJoinedTeamUser(teamId);
                    //出现了几次就相当于队伍有几个人
                    if (peopleNumTeam >= joinTeam.getMaxNum()) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已满");
                    }
                    //不能重复加入已加入的队伍
                    qw = new QueryWrapper<>();
                    qw.and(q -> q.eq("team_id", teamId).eq("user_id", userId));
                    UserTeam userJoinTeam = userTeamService.getOne(qw);
                    if (userJoinTeam != null) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "不能加入重复队伍");
                    }
                    //todo 修改队伍人数+1
                    //todo 如果要退出队伍，先把队长转给其他人

                    //插入到用户队伍表
                    UserTeam userTeam = new UserTeam();
                    userTeam.setUserId(userId);
                    userTeam.setTeamId(teamId);
                    userTeam.setJoinTime(new Date());

                    boolean result = userTeamService.save(userTeam);
                    return result;
                }

            }

        } catch (InterruptedException e) {
            return false;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                //只能释放当前线程加的锁
                lock.unlock();

            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean quitTeam(TeamQuitDTO teamQuitDTO, Userinfo loginUser) {
        //实现退出队伍功能
        //1.校验参数合法性
        if (teamQuitDTO == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        Long teamId = teamQuitDTO.getTeamId();
        //判断<= 0可以适当规避一下缓存穿透
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        //2.验证用户是否已经加入该队伍，如果没有加入则不能退出
        Long loginUserId = loginUser.getId();
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(loginUserId);
        userTeam.setTeamId(teamId);
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>(userTeam);
        UserTeam userJoinTeam = userTeamService.getOne(queryWrapper);
        if (userJoinTeam == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "你未加入|创建该队伍");
        }

        //3.判断退出队伍前 队伍人数是否为1，如果为1的话则直接解散队伍
        long joinTeamUserNum = getJoinedTeamUser(teamId);
        if (joinTeamUserNum == 1l) {
            //队伍解散（删除队伍信息，删除队伍以及用户关系表的数据）
            //因为只剩一个人了，所以只需判断teamid即可
            this.removeById(teamId);
        } else {
            //队伍还剩至少两人
            //4.判断用户是否为队长
            if (team.getUserId().equals(loginUserId)) {
                //是的话，要先将队长身份转出在可以退出
                QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper();
                userTeamQueryWrapper.eq("team_id", teamId);
                //last是指在生成的SQL语句后加入指定的语句
                userTeamQueryWrapper.last("order by id limit 2");
                //查询已加入用户和加入时间
                List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
                if (CollectionUtils.isEmpty(userTeamList) || userTeamList.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                //取出即将成为队长的用户的id
                UserTeam nextUser = userTeamList.get(1);
                Long nextUserId = nextUser.getUserId();
                Team updTeam = new Team();
                updTeam.setId(teamId);
                updTeam.setUserId(nextUserId);
                boolean result = this.updateById(updTeam);
                if (!result) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "转移队长失败");
                }
            }


        }//移除关系表中的数据
        //退出队伍
        //否则，可以直接退出,删除关系表的相关记录即可
        return userTeamService.remove(queryWrapper);
    }

    //todo 解散队伍的复用

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean dissolveTeam(TeamQuitDTO teamQuitDTO, Userinfo loginUser) {
        //1.校验参数合法性
        if (teamQuitDTO == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        Long teamId = teamQuitDTO.getTeamId();
        //判断<= 0可以适当规避一下缓存穿透
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        //2.验证用户是否是队长
        Long loginUserId = loginUser.getId();
        if (!team.getUserId().equals(loginUserId)) {
            throw new BusinessException(ErrorCode.NO_AUTH, "你不能解散其他人队伍");
        }
        //解散队伍，删除队伍信息，删除队伍用户关系表的信息

        UserTeam userTeam = new UserTeam();
        userTeam.setTeamId(teamId);
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>(userTeam);
        boolean result = userTeamService.remove(queryWrapper);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除队伍失败");
        }
        return this.removeById(teamId);
    }

    @Override
    public List<Team> getMyTeam(Userinfo loginUser) {
//        Long loginUserId = loginUser.getId();
//        //条件查询
//        QueryWrapper<UserTeam> qw = new QueryWrapper<>();
//        qw.eq("user_id", loginUserId);
//        List<UserTeam> userTeamList = userTeamService.list(qw);
//        //获取加入的队伍的所有id
//        List<Long> teamIds = new ArrayList<>();
//        List<UserVO> joinTeamUsers = new ArrayList<>();
//        List<TeamVO> teamVOList = new ArrayList<>();
//        //为队伍添加加入用户以及队长
//        userTeamList.forEach(userTeam -> {
//            TeamVO teamVO = new TeamVO();
//            UserVO userVO = new UserVO();
//            Long teamId = userTeam.getTeamId();
//            teamIds.add(teamId);
//            //获取用户id
//            Long teamUserId = userTeam.getUserId();
//            //判断是否为队伍的创建人
//            if (!loginUserId.equals(teamUserId)) {
//                //如果不是，则查数据库将数据取到
//                Userinfo joinTeamUser = userinfoService.getById(teamUserId);
//                UserVO joinTeamUserVO = new UserVO();
//                BeanUtils.copyProperties(joinTeamUser, joinTeamUserVO);
//                joinTeamUsers.add(joinTeamUserVO);
//            }
//            teamVO.setUserVOS(joinTeamUsers);
//            teamVO.setCreateUser(userVO);
//            teamVOList.add(teamVO);
//        });
//        List<Team> teams = this.listByIds(teamIds);
//
//
//        return teams;
        return null;
    }

    /**
     * 封装的查询队伍人数的方法
     *
     * @param teamId
     * @return
     */
    private long getJoinedTeamUser(long teamId) {
        QueryWrapper<UserTeam> qw = new QueryWrapper<>();
        qw.eq("team_id", teamId);
        long peopleNumTeam = userTeamService.count(qw);
        return peopleNumTeam;
    }
}




